\chapter*{Трюки, ухищрения или старинное китайское искусство Chi Ting – в массы.}
  

 Вы слышали раньше про старинное китайские искусство
    Chi Ting? Первая информация об исследованиях этого искусства
    появилась в 1988 году в статье Джима Блинна (того самого, который
    Blinn shading model) под названием "THE ANCIENT CHINESE ART OF
    CHI-TING" \footnote{http://research.microsoft.com/users/blinn/CHITING.HTM},
    однако Джим указал, что первенство в придумывании названия для
    этого артефакта принадлежит Биллу Этре. Первое же упоминание об
    использовании данного искуства в отношении Renderman, следует
    полагать, появилось в Siggraph-92 Renderman Course Notes (ну вот
    как это название на русский перевести прикажете, а? – пусть будут
    Материалы Учебного Курса по Renderman)
  

 Ладно, ладно, я больше не буду издеваться –
    название Chi Ting, если его прочитать в соответствии с правилами
    английского языка, созвучно английскому же слову cheating – то
    есть, жульничать, надувать.
  

 Работая в Лаборатории Реактивного Движения (NASA
    Jet Propulsion Laboratory) над анимационным роликом, рассказывающем
    о пролёте станции исследования дальнего космоса Вояджер над
    Сатурном – вы наверняка видели этот ролик – Джимм заметил, что, на
    самом деле, зрителя мало интересует тот факт, что созданная вами
    анимация была построена на точных физических принципах и отображает
    истинную картину мира. Более того, использовать “взрослую физику”
    при производстве такой анимации зачастую попросту невозможно (в
    силу нашего незнания, неготовности или физической невозможности
    проводить расчёты такой сложности). Обратите внимание – я не говорю
    про физические эксперименты и визуализацию их результатов, я говорю
    про Вояджер, красиво пролетающий мимо колец Сатурна.
  

 Так почему бы в таком случае не срезать несколько
    углов и не обмануть зрителя, упростив расчёты или изменив алгоритм,
    например? Если в стакане красиво плещется вода, то
    среднестатистическому посетителю кинотеатра (а
    телерекламосмотрителю – и подавно) всё равно, настоящая там вода
    была в стакане, сымитированная на суперкомпьютере при помощи
    суперпродвинутых метафизических über-формул – или это просто
    “грязный хак”, который просто выглядит как вода в
    стакане.
  

 Для
    любознательных: буквально на днях
    прочитал весьма поучительную историю о том, как делаются рекламные
    фотографии сухих молочных завтраков – на языке потенциального
    противника они называются crisps, у нас пусть будут - мюсли.
    Общеизвестно, что мюсли – штука катастрофически хрупкая, и обычно
    на донышке пачки вместо той красотищи, что изображена на коробке,
    обнаруживается лишь сухое мелкодисперсное месиво. Так вот для того,
    чтобы сделать фотографию для оформления коробки, на пол высыпают
    целый ящик мюслей, после чего вручную ювелирными щипчиками отбирают
    из огромной кучи 50-60 абсолютно целых жирных кусочков (обычно к
    моменту начала ковыряния в куче их где-то столько целых и
    остаётся). Берётся сосуд, в него опять же вручную, по одному
    укладываются отобранные ранее кусочки, затем промежутки заполняются
    ярко-белым гелем для волос (в отличие от клея, он не портится от
    яркого света и высокой температуры, создаваемых софитами) – это
    будет видимость молока. В зависимости от сорта сухого завтрака
    добавляются всякие вишенки или шоколадки – на самом деле внутри
    коробки их нет, но на фотографии они обязательно будут. И вот уже
    эта несъедобная конструкция фотографируется.
  

 Как мне кажется, стандарт Renderman и
    поддерживающие его рендереры являются средствами, которые чуть ли
    не подталкивают пользователя к оптимизации сцены и использованию
    всевозможных ухищрений. Сочетание простого текстового формата,
    обширных возможностей настройки качества и скорости рендеринга и
    достаточно высокая скорость рендеринга дают возможность постоянно
    экспериментировать с различными настройками, добиваясь оптимального
    баланса качества и скорости. Как уже стало заведено в этой главе,
    мы не будем сильно углубляться в дебри настроек и фокусов, показав
    лишь наиболее очевидные вещи и в некоторых случаях проиллюстрировав
    эти трюки простыми примерами.\hfil\break
    И начнём мы с действительно простых вещей – а именно некоторых
    особенностей Renderman-совместимых рендереров.
  \chapter*{ShadingRate}
  

 В RenderMan существуют несколько настроек качества
    изображения, которые являются критическими и без знания о которых
    вы не сможете добиться нормального баланса между временем расчёта и
    качеством картинки. Основная такая настройка – это
    ShadingRate.
  

 Prman, равно как и многие другие рендереры,
    накладывает шейдеры после тесселяции объекта на микрополигоны -
    будем называть их сэмплами. Если посмотреть на этот вопрос немного
    упрощённо, то ShadingRate – это то количество пикселей, которое
    рендерер будет просчитывать за один вызов шейдера. Таким образом,
    вызов
  

ShadingRate 0.25
  

 означает, что на каждый пиксель картинки шейдер
    будет вызван приблизительно 4 раза (1/0.25) в различных точках
    внутри этого пикселя, а полученные значения будут усреднены и,
    соответственно, качество картинки будет гораздо выше. Установка же
    такого значения:
  

ShadingRate 20
  

 означает, что вызов шейдера будет производиться
    один раз на примерно 20 пикселей, что даст гораздо худшее качество
    – но рендеринг завершится очень быстро. Так вот, поскольку именно
    шейдинг в процессе обсчёта картинки занимает бОльшую часть времени
    (в особенности для сложных процедурных шейдеров, например, с
    нойзом), то эта настройка является самой часто используемой в
    Renderman.
  

 Давайте рассмотрим простой пример. Откроем простой
    RIB с шариком, с которым мы экспериментировали на протяжение всей
    нашей главы, и настроим в нём величину ShadingRate (файл на диске
    под именем shadingrate025.rib):
  

Display "RenderMan"
    "framebuffer" "rgb"
  

Format 256 192 1
  

ShadingRate 0.25
  

Translate 0 0
    2.7650300856
  

WorldBegin
  

  TransformBegin
  

    LightSource
    "ambientlight" 500
  

     "lightcolor" [0.051
    0.051 0.051]
  

    LightSource
    "distantlight" 501 "from" [1 1.5 -1] "to" [0 0 0]
  

    LightSource
    "distantlight" 502 "lightcolor" [0.2 0.2 0.2]
  

                "from" [-1.3 -1.2 -1.0] "to" [0 0 0]
  

  TransformEnd
  

  Surface "plastic"
  

  Sphere 1 -1 1 360
  

WorldEnd
  

 Напускаем prman на этот файл и получаем в результате:
  

 \gr{image117}
  

 Для
    продвинутых: в этом примере вы
    использовали ShadingRate, равный 0.25, то есть 4 вызова шейдера для
    каждого пикселя. По умолчанию, если величину ShadingRate в сцене не
    указывать, то она будет выставлена в 0.5. Большинство экспортеров
    трехмерной сцены  в
    RIB,  и SLIM в их
    числе, специально выставляют ShadingRate – об этом следует помнить,
    особенно в процессе работы в SLIMе (выставляющем по умолчанию 5.0),
    когда вам начинает казаться, что качество картинки в окне
    предварительного просмотра не очень высокое.
  

 Изменяем значение ShadingRate (файл на диске
    shadingrate64.rib):
  

Display "RenderMan"
    "framebuffer" "rgb"
  

Format 256 192 1
  

ShadingRate 64
  

Translate 0 0
    2.7650300856
  

WorldBegin
  

  TransformBegin
  

    LightSource
    "ambientlight" 500
  

                "lightcolor" [0.051 0.051 0.051]
  

    LightSource
    "distantlight" 501 "from" [1 1.5 -1] "to" [0 0 0]
  

    LightSource
    "distantlight" 502 "lightcolor" [0.2 0.2 0.2]
  

                "from" [-1.3 -1.2 -1.0] "to" [0 0 0]
  

  TransformEnd
  

  Surface "plastic"
  

  Sphere 1 -1 1 360
  

WorldEnd
  

 После рендеринга видим:
  

 \gr{image119}
  

 Рендеринг закончился гораздо быстрее, но качество
    результата оставляет желать лучшего – шейдер вызывался один раз на
    64 пикселя и теперь вы видим границы между сэмплами.
  

 Существует простой способ сгладить эти неприятные
    грани, добавив в RIB-файл где-нибудь рядом с ShadingRate
    строку
  

ShadingInterpolation  "smooth".
  

 Prman после этого будет интерполировать значения
    соседних шейдинг сэмплов, и наш шарик вновь станет гладким. Но на
    примере с текстурой из файла будет очевидно, что изображение
    текстуры от этого чётче не стало, а всего лишь «размазалось» по
    поверхности.
  

 Для
    продвинутых: как понять, насколько
    быстрее закончился рендеринг? Можно посидеть над компьютером с
    секундомером или песочными часами в руках или написать скрипт,
    который будет отсчитывать время за вас. А можно вставить в RIB
    опцию вывода статистики:
  

Option "statistics"
    "endofframe"
    [1]
  

 {\it или}
  

Option "statistics"
    "endofframe"
    [2]
  

 в зависимости
    от того, насколько детальная информация вас интересует. Для наших
    двух примеров оказывается, что количество сэмплов, которое prman
    создал в процессе рендеринга для каждой из сцен, равняется 1030 для
    первой сцены и всего лишь 56 – для второй. Экономия памяти
    составила 1 мегабайт; экономия времени – несколько раз (поскольку
    картинка маленькая и простая, то сосчитать точно количество раз
    сложно даже с такой статистикой – для второй сцены она показала 0
    секунд).
  

 Особенностью Renderman, которая позволяет очень
    гибко использовать ShadingRate, является тот факт, что почти все
    переменные в сцене можно указывать как глобально – один раз на
    сцену – так и локально – для каждой модели и чуть ли не для каждого
    полигона. Делается это очень просто, например, вот так (приведен
    небольшой фрагмент RIB-файла):
  
\begin{lstlisting}[frame=single, framerule=0pt, framesep=10pt, xleftmargin=10pt, xrightmargin=10pt]
ShadingRate 0.25 # Это значение будет работать для всей сцены
AttributeBegin
ShadingRate 4 # Кроме этого шарика, для которого
              # определено своё собственное значение
Surface "plastic"
Sphere 1 -1 1 360
AttributeEnd
\end{lstlisting}
  

 Таким образом, вы можете установить высокое
    значение ShadingRate для той части сцены, которая находится близко
    к камере, и низкое – для дальних или несущественных объектов. Более
    того, используя Mel, это значение можно сделать динамически
    зависящим от расстояния от объекта до камеры.
  

 Для
    продвинутых: Опытные TD советуют
    использовать для просчёта карт теней высокие значения ShadingRate,
    вплоть до 32 (но всё же не больше 8, если используется
    displacement). Как оказалось, на видимое качество теней это почти
    не влияет, а ускорение просчёта составляет до 20
    раз.
  

 К сожалению,
    этот трюк хорошо работает не для всех типов теней; также его не
    рекомендуется применять для key light и областей с сильным
    дисплейсментом из-за возможности появления “грязи”.Сами Pixar также
    советуют при использовании motion blur увеличивать ShadingRate –
    поскольку объекты и так будут “разблюрены”, то небольшое падение
    качества к видимому ухудшению картинки не приведёт.
  

 По опыту
    крупных зарубежных студий, скорее всего, к моменту окончательного
    рендеринга у вас не будет большого выбора в значениях ShadingRate,
    и тому есть несколько причин. Во-первых, поддержание единого
    значения ShadimgRate в сцене позволяет избежать разнобоя в картинке
    – грубо говоря, артефакты на всех моделях выглядят одинаково.
    Далее, модели с высокочастотными текстурами (особенно с большим их
    количеством) очень требовательны к ShadingRate. И наконец, даже
    если объект движется (и мы скрываем его при помощи motion blur и,
    по совету Pixar, увеличиваем SR), всё равно будут несколько кадров,
    к которых он будет неподвижен – и именно эти кадры должны показать
    работу шейдерописателей  и текстурных художников во всей красе
    – потому что именно на этих кадрах (согласно закону бутерброда)
    будут делать стоп-кадры на своих DVD-проигрывателях
    будущие трёхмерщики – и изучать, изучать, изучать...
 
 \section*{PixelSamples}
  

 Архитектура REYES, на которой построен prman, в
    момент своего создания сильно отличалась от общепринятых подходов к
    трехмерному рендерингу, в частности, из-за того, что процесс
    шейдинга в ней происходит до определения видимости и вычисления
    краевого антиалиасинга поверхностей.
  

 Как это происходит? Получается, что на какой-то
    стадии шейдеры вызваны, объекты уже закрашены, а картинка всё ещё
    девственно чиста? Что же тогда «красят» шейдеры?
  

 Всё очень просто: закраска происходит не в
    двумерном пространстве картинной плоскости, а в трехмерном мире
    сцены. Дело в том, что ещё до шейдинга prman производит тесселляцию
    (разбиение) видимых поверхностей на микрополигоны, размеры которых
    приблизительно определяются числом занимаемых ими на картинной
    плоскости пикселей – да-да, ShadingRate как раз тут и говорит свое
    веское слово. После такой тесселляции в распоряжении рендерера
    оказывается сетка из микрополигонов, которые, с одной стороны,
    являются геометрическими объектами – малюсенькими прямоугольными
    патчами; с другой стороны, их можно рассматривать как шейдинг
    сэмплы - минимальные кусочки поверхностей, закрашиваемые сплошным
    цветом.
  

 Вот здесь и начинается собственно шейдинг: для
    каждого такого патчика будет сформирован  однократный вызов соответствующих
    шейдеров и каждый патчик получит свой цвет и прозрачность. А если
    на поверхность назначен и displacement шейдер – патчики-сэмплы
    будуть сдвинуты в  указанном шейдером направлении. Но, повторяю, это все пока
    происходит в трехмерном пространстве, до картинной плоскости дело
    еще не дошло!
  

 Следующая задача – спроецировать эту трехмерную
    информацию на пиксели картинки. Тут вроде бы всё просто – нужно
    только грамотно определить видимость шейдинг сэмплов из пикселей
    картинки, чтобы определить, какой шейдинг сэмпл закрасит какой
    пиксель.
  

 В реальности же всё намного сложнее, в основном,
    из-за того, что тут мы попадаем в царство Его Величества
    Антиалиасинга. Не вдаваясь в подробности, скажу лишь, что
    для  этой цели алгоритм
    REYES использует oversampling, то есть многократный точечный
    сэмплинг. Через каждый пиксель картинной плоскости (грубо говоря –
    нашего конечного изображения) провешивается несколько лучиков
    (точнее будет сказать, point samples, или точечных сэмплов, – не
    путать с шейдинг сэмплами, с одной стороны, и со световыми лучами
    при рейтрейсинге – с другой стороны!) к сцене, и выясняется, в
    шейдинг сэмплы с каким цветом/прозрачностью они упёрлись. Эти
    значения взвешиваются по каждому пикселю, с использованием одной из
    нескольких доступных для пользователя фильтрующих функций и в
    зависимости от удаленности лучиков от центра пикселя. Результаты
    взвешивания записываются в пиксель готовой картинки.
  

 Так вот, параметры PixelSamples определяют
    количество точечных сэмплов на каждый пиксель – по вертикали и
    горизонтали. PixelSamples 5 5 заставит prman запросить
    цвет/прозрачность шейдинг сэмплов, спроецировааных в каждый пиксель
    ровно 5 х 5 = 25 раз.
  

 Если копнуть ещё глубже, то выяснится, что в
    реальности Prman провешивает лучики, не разбивая каждый пиксель на
    регулярную сетку, размером, скажем, 5х5, как в нашем примере, а
    случайным образом разбрасывая их в пределах ячеек этой сетки. По
    науке это называется Stochastic Sampling – именно на эту технологию
    Pixar получила свой патент, и именно при помощи этого патента
    засудила компанию Exluna.
  

 Интересно, что такие эффекты как Motion Blur и
    Depth of Field prman-ом производятся как раз при помощи
    PixelSamples. Всё очень просто: направления PixalSample-лучей
    опять-таки случайным образом изменяются c учетом вектора «движения»
    шейдинг сэмплов и/или их удаленности от «точки фокуса» камеры.
    Собственно, вот вам и объяснение того, что prman держит пальму
    первенства в скорости рендеринга Motion Blur – там, где
    другие рендереры при включении MB замедляются в
    разы, он теряет всего десяток-другой процентов, потому что
    продолжает использовать те же самые алгоритмы, что и до этого. И
    именно поэтому сцены с motion blur являются
    более  требовательными к PixelSamples, чем
    обычные – ведь чем сильнее блюр, тем больше может потребоваться
    сэмплов, чтобы размазанность выглядела равномерно, а не
    “разбросанной по экрану”.\hfil\break
    Ну и последнее уточнение, без которого описанная выше схема
    рендеринга не имеет никакого практического значения: алгоритм REYES
    потребует колоссального количества памяти, если попытаться
    реализовать его, начиная с тесселляции поверхностей, для всей
    картинки сразу. К счастью, он прекрасно работает и с ее частями
    (помните, я рассказывал про бакеты?), по умолчанию, размером 16х16
    пикселей, из которых потом, как из мозаики, собирается все
    изображение.
  

 Ффух. Отдышались. Поехали дальше.
  

 По умолчанию величина PixelSamples в prman
    установлена как:
  

PixelSamples 2
    2
  

 Это означает, что на каждый пиксель приходится 4
    точечных сэмпла . Если мы изменим эту величину (файл на диске
    pixelsamples11.rib):
  

Display "RenderMan"
    "framebuffer" "rgb"
  

Format 256 192 1
  

ShadingRate 64
  

PixelSamples 1 1
  

Translate 0 0
    2.7650300856
  

WorldBegin
  

  TransformBegin
  

    LightSource
    "ambientlight" 500
  

                "lightcolor" [0.051 0.051 0.051]
  

    LightSource
    "distantlight" 501 "from" [1 1.5 -1] "to" [0 0 0]
  

    LightSource
    "distantlight" 502 "lightcolor" [0.2 0.2 0.2]
  

                "from" [-1.3 -1.2 -1.0] "to" [0 0 0]
  

  TransformEnd
  

   Surface "plastic"
  

   Sphere 1 -1 1 360
  

WorldEnd
  

 то получим картинку без антиалиасинга и с видимыми
    проблемами с шейдингом – но получим её очень быстро:
  

 \gr{image121}
  

 Управляя двумя переменными – ShadingRate (для любых
    участков вашей сцены) и PixelSamples (для всей сцены) – можно очень
    гибко настраивать рендеринг, добиваясь максимального качества за
    минимальное время.
  

 Для
    продвинутых: покажем, как управлять
    этими параметрами при помощи утилиты cpp – как мы отмечали раньше,
    такая утилита есть в поставке почти всех рендереров, совместимых с
    Renderman. Для этого внесём изменения в RIB (вы можете просто
    открыть cpptest.rib с диска):
  

Display
    "RenderMan" "framebuffer" "rgb"
  

Format
    256 192 -1
  

ShadingRate
    SR
  

PixelSamples
    SX SY
  

Translate
    0 0 2.7650300856
  

{\it WorldBegin}
  

{\it    TransformBegin}
  

     LightSource "ambientlight"
    500
  

{\it                  "lightcolor" [0.051 0.051 0.051]}
  

     LightSource "distantlight" 501 "from" [1
    1.5 -1] "to" [0 0 0]
  

     LightSource "distantlight" 502
    "lightcolor" [0.2 0.2 0.2]
  

{\it                  "from" [-1.3 -1.2 -1.0] "to" [0 0 0]}
  

{\it    TransformEnd}
  

{\it    Surface "plastic"}
  

{\it   }  Sphere 1 -1 1 360
  

{\it  WorldEnd}
  

 Мы определили
    внутри файла три символьные константы – SR, SX и SY. Теперь вызовем
    в командной строке препроцессор:
  

cpp  -DSX=2
    -DSY=2
    -DSR=0.25 cpptest.rib  | prman
  

 и получим в
    результате картинку с необходимыми установками – константы внутри
    файла будут заменены на значения, объявленные при вызове
    cpp.
  

 Несколько
    неожиданный ход – использовать препроцессор языка C++, которым
    обычно обрабатываются файлы SL, для работы с файлов RIB. В этом вся
    соль расширяемости Renderman – поскольку и те, и другие файлы
    являются текстовыми, то для их обработки годятся одни и те же
    утилиты.

  \section*{LOD}
  

 Ещё одним трюком из арсенала читинга в Renderman
    является Level of Details, или сокращенно LOD.
  

 Смысл данной настройки состоит в том, что в RIB
    файле можно указывать не одну геометрическую модель, а несколько, и
    при этом указать, начиная с какого расстояния до камеры
    использовать ту или иную модель в данном месте сцены. Представьте
    себе битву с участием сотен сражающихся. Без LOD всё, что вы могли
    бы сделать – это использовать одну и ту же высококачественную
    модель как для бойцов первого плана, так и для статистов на заднем
    плане и что совсем печально – для огромной массы бойцов на
    горизонте, каждый из которых отрендерится максимум в десяток
    пикселей. Применяя LOD, вы можете автоматически подставлять для
    переднего плана – максимально детальную и проработанную модель; для
    среднего плана – что-то немного упрощённое; для массовки на заднем
    плане – палку-палку-огуречик или вообще один полигон с наложенной
    текстурой – и тем самым сильно уменьшить запросы по памяти и
    времени расчёта – а разницу никто не заметит.
  

 Особенно привлекательным представляется
    использование LOD в случае большого количества одинаковых или почти
    одинаковых объектов (толпа, деревья в лесу), но этот финт также
    активно используется при архитектурной визуализации (с её огромными
    и очень детальными моделями зданий на переднем плане), особенно при
    анимации внутри городских джунглей.
  

 Для
    продвинутых: как отголосок этой
    техники, иногда полезно иметь не только несколько моделей с
    различной детализацией проработки, но и различные наборы шейдеров и
    текстур – один набор, скажем, для первого плана, другой набор – для
    статистов вдалеке от камеры. Поверьте, дополнительное время,
    потраченное на настройку нескольких таких наборов, вернётся
    сторицей на этапе финального рендеринга.
  
\section*{Архивы и Замороженные Архивы.}
  

 В примере для продвинутых мы показали, что для
    обработки файлов RIB можно использовать такую утилиту, как
    препроцессор языка программирования C++, через который и так
    проходят все шейдеры перед компиляцией. Одной из возможностей
    препроцессора являются так называемые вложенные файлы (includes) –
    мы уже пробовали их при рассмотрении шейдеров. Аналогично вставке
    кода из файлов *.H в файлы *.SL при помощи препроцессора, мы можем
    вставлять RIB-файлы друг в друга.
  

 Вполне логично предположить, что в стандарте RIB
    существует аналогичная функциональность. Так оно и есть – вы можете
    вставить один RIB в другой (как кусок текста) при помощи команды
    ReadArchive, например, вот так:
  

 ReadArchive "templates/teapot.inc"
  

 А в файле teapot.inc, находящемся в директории
    templates, будет находиться необходимая геометрия (в нашем случае,
    чайник). Вынеся таким образом геометрию в отдельный файл, вы можете
    экспериментировать с глобальными настройками сцены, не опасаясь
    что-либо испортить в модели.
  

 В Maya нечто подобное под названием instancing
    появилось лишь в версии 6.5; в Renderman эта опция существует уже
    более 20ти лет.
  

 Но стандарт Renderman не остановился на достигнутом
    и ввёл дополнительную возможность под названием
    DelayedReadArchive.
  

Procedural  "DelayedReadArchive"
    [ "small.rib"
    ] [-1 1 -1 1 -1 1]
  

 Работает этот вызов в точности как и предыдущий, с
    одним важным отличием – в нём указана область нахождения данного
    объекта в пространстве (в просторечии, bounding box). Так вот,
    загрузка объекта в память рендерера произойдёт только в том случае,
    если область объекта пересечётся с видимой областью камеры, и,
    следовательно,  только
    тогда, когда расчёты сцены дойдут до этой области.
  

 В точности такую же философию проповедуют и
    процедурные генераторы RIB-кода, вызов которых можно осуществлять
    таким образом:
  
\begin{lstlisting}[frame=single, framerule=0pt, framesep=10pt, xleftmargin=10pt, xrightmargin=10pt]
Procedural "RunProgram" [ "teapot.pl" "some_teapot1_data" ] [-1 1 -1 1 -1 1 ]
\end{lstlisting}
  

 Код нашего скрипта на Перле вызовется только в том
    случае, если он попадёт в поле зрения камеры, и только тогда, когда
    это необходимо.
  

 Для
    любознательных: именно эту технологию
    применила студия Dreamworks при съёмках мультфильма Shark Tale. В
    силу производственной необходимости, вся геометрия для этого
    мультфильма подготавливалась в внутреннем формате студии, не
    совместимом с prman (для совсем продвинутых – это были специальные
    NURBSы). Поэтому был написан процедурный конвертер из этого
    формата; вызовы этого конвертера были вставлены в обычные RIBы.
    Таким образом, на диске геометрия хранилась в удобном для
    Dreamworks формате и преобразовывалась в RIB только во время
    рендеринга – и только в том случае, если в этом была
    необходимость.
  

 Возможности стандарта Renderman в области
    процедурной генерации геометрии дадут серьёзную фору многим
    существующим рендерерам. А мы тем временем обратимся к экзотическим
    возможностям prman.
 
 \section*{Разговаривающие и адаптивные шейдеры}
  

 Одной из новых возможностей, реализованных в
    Renderman Pro Server раньше, чем это появилось в стандарте,
    является возможность передавать сообщения между шейдерами (message
    passing). В рамках этой функциональности один шейдер может
    запросить значение переменной, определённой в другом шейдере,
    наложенном на эту же модель.
  

 Классический пример использования message passing –
    получение surface-шейдером информации из displacement-шейдера о
    величине отклонения и соответствующая закраска поверхности –
    например, более выпуклые области модели закрашиваются более тёмным
    цветом. Связывать таким образом можно любые шейдера, относящиеся к
    одному объекту – например, поставив displacement в зависимость от
    освещённости. Сложная на первый взгляд, передача сообщений между
    шейдерами предоставляет ещё один метод тонкой настройки отображения
    поверхностей, в том числе возможность реализовать адаптивные
    шейдеры – оптимизирующие свою функциональность непосредственно во
    время просчёта.
  

 Для
    продвинутых: простой пример
    адаптивного шейдера. В процессе расчёта ambient occlusion мы в
    каждом сэмпле выстреливаем некоторое количество лучиков в
    полусферу, причём для того, чтобы в картинке не было грязи и чтобы
    при анимации она не дрожала, это некоторое количество обычно
    является достаточно большим – как минимум, 256. Поскольку говорим
    мы о ray tracing, то любая оптимизация этого процесса будет к
    месту. Способ адаптации шейдера к свойствам поверхности, на которой
    он вызван, состоит в том, чтобы смотреть на локальную кривизну
    поверхности в данной точке (например, спрашивая об этом
    displacement shader, или просто вычисляя ее по изменению текстурных
    координат и их производных) и если кривизна большая – то в этой
    точке стрелять лучиков меньше (всё равно за мелкими деталями грязи
    видно не будет), а если небольшая – то побольше.
  

 Ещё один
    простой пример, чтобы немного взбударажить фонтазию нашего
    читателя. Попробуйте поуправлять величиной спекуляра или размером
    дисплейсмента в зависимости от расстояния до камеры. Или попробуйте
    наложить дисплейсмент только на края объекта, ограничившись в
    остальной части более примитивным и скоростным
    бампом.
  

 Для
    продвинутых-2: MTOR поддерживает
    мощный механизм для связи майских атрибутов с шейдерами. Чтобы
    воспользоваться этим механизмом, достаточно добавить к любому
    майскому объекту новый атрибут с именем, например, rmanF{\it myS} {\it  }{\it Приставка} {\it rmanF} (таких приставок
    несколько и они подробно описаны в документации) даёт MTORу сигнал, что
    данный атрибут должен быть обработан специальным образом и его
    значение должно быть подготовлено к экспорту. Всё, что остаётся
    сделать – это использовать полученное значение в шейдере, например,
    вот так:
  
surface test( varying float myS = 0; )

  
{
  
  
   Ci = color(myS);

  
}

  

 Ну вот, по внутренностям прошлись – а теперь
    настало время от оптимизации и ухищрений на микроуровне перейти к
    чистой воды читингу на макроуровне, а также просто полезным
    советам. Если предыдущие хинты имели смысл по большей части только
    для Renderman-совместимых рендереров, то теперь мы постараемся быть
    полезными и для других движков, в том числе для Mental Ray и самой
    Maya. Настало время для крупнокалиберной артиллерии и грязных
    трюков.

  \section*{Только то, что видно (Стыдно – когда видно)}
  

 Этот приём очевиден, но удивительно, насколько
    редко он применяется на практике. Не нужно включать в просчёт то,
    чего всё равно никто не увидит!
  

 Если какая-то часть сцены не попадает в камеру и не
    участвует в отражениях или в ambient occlusion – удаляйте её,
    вручную или при помощи скриптов. Если вместо огромного города,
    смоделированного до последнего гвоздика на подоконнике, вполне
    пройдёт текстура или matte-рисунок на заднике – удаляйте город.
    Считаете тени? Удаляйте всё, что не видно из этого источника света.
    Считаете отражения? Удаляйте всё, что по другую сторону зеркала. В
    конечном счёте, рендерер это сделает и сам – но зачем заставлять
    его прокачивать гигабайты мусора, из которого не получится ни
    одного полезного пикселя?
  

 Особую пользу этот совет принесёт вам в случае,
    если вы используете RAT. MTOR известен своей {\it втыкучестью} (слово нехорошее,
    нелитературное, но зато очень хорошо иллюстрирующее суть процесса)
    в момент экспорта сцены; небольшой скрипт на Mel, который
    пробежится по сцене перед экспортом и выключит ту геометрию,
    которая в данный момент из камеры не видна, ускорит генерацию RIBов
    для действительно сложных сцен  на порядки.
  
\section*{По слоям}
  

 Рендеринг “по слоям” уже давно стал обязательным
    требованием работы в продакшн хаусах. Обычно в отдельные файлы
    (“слои”) выводятся все персонажи; отдельно от них считаются
    спецэффекты (огонь, вода и прочие медные трубы); отдельно
    записываются environment. Затем все эти отдельные слои сводятся
    воедино уже на этапе композинга; это снимает ограничения на
    всевозможные настройки освещения и удаление-добавление персонажей в
    сцену, которые так любят проделывать заказчики в самый последний
    момент.
  

 Более того, в большинстве студий в отдельные слои
    выводятся не только персонажи, но тени от них, карты нормалей для
    персонажей, Z-buffer – толковые двухмерщики всегда найдут, где
    применить эту информацию, благо на время рендеринга сцены это
    влияет не сильно.
  

 Более того, Renderman даёт возможность выводить в
    отдельные изображения переменные, определённые внутри шейдеров –
    как глобальные (например, нормаль поверхности, цвет поверхности),
    так и локальные (например, диффузную составляющую освещённости
    конкретного персонажа). Остановимся на этом финте ушами
    поподробнее.
  

 Откройте в Maya сцену, которую мы использовали при
    тестировании RATа – ту самую, с овечкой (файл step2.ma вполне
    подойдёт). Убедитесь, что всё в порядке и сцена нормально
    рендерится – RenderMan=>Render.
  

 Воспользуемся встроенными возможностями MTOR для
    того, чтобы показать, как вывести в отдельный слой информацию о
    цвете модели, без учёта её освещённости. Для этого вызываем окно
    настроек RenderMan=>RenderMan Globals…, в нём в табе Display
    переключаемся в локальный таб второго уровня Secondary:
  

 \gr{image123}
  

 Выбираем канал, который мы хотели бы вывести
    -  нажимаем на кнопку
    New, после этого настраиваем параметры, как указано на
    скриншоте:
  

 \gr{image125}
  

 Что мы с вами только что сделали? Мы указали, что
    хотим отрендерить в отдельный файл значение Cs (выбранное из
    длинного списка доступных переменных в поле Mode), которое в языке
    шейдеров соответствует цвету объекта до того, как мы в шейдере
    применили к нему какие-то операции. Вызываем рендерер и видим на
    экране:
  

 \gr{image127}
  

 Обычный результат нашего рендеринга. Где же
    подевался запрашиваемый нами дополнительный слой? Он находится в
    поддиректории rmanpix вашего проекта. Посмотрим внутрь файла
    untitled.new\_channel.0001.tif, появившегося в этой директории, при
    помощи утилиты sho из поставки Renderman Pro Server:
  

sho untitled.new\_channel.0001.tif
  

    Тот, кто до сих пор шарахается от командной строки, может
    использовать подростковую программу типа ACDSee.
  

 \gr{image129}
  

 Как видно, перед нами почти готовая заливка для
    мультипликационного персонажа.
  

 Для
    продвинутых: чтобы получить ещё и
    картунную обводку (cartoon outline), выведите в виде отдельного
    слоя карту нормалей и преобразуйте ее в любимом композере при
    помощи фильтра Sobel. Вы получите достаточно примитивный и не
    всегда удачный, но вполне качественный контур, особенно если
    отрендерите картинку в двойном разрешении, а потом уменьшите её
    обратно – это скроет возможные артефакты. По методам
    нефотореалистического рендеринга с помошью prman можно написать
    пару толстых и тяжёлых книг, но мы ограничимся тремя фразами в этой
    вставке для любознательных – в надежде подтолкнуть вас в нужном
    направлении.
  

 Как-нибудь на досуге поиграйтесь с другими
    переменными из списка Mode; особый интерес представляет переменная
    uniform float \_\_CPUtime, которая в виде картинки покажет вам, на
    рендеринг каких части вашей модели тратится больше всего времени. А
    мы тем временем собираемся узнать, как вывести в отдельный слой
    произвольную переменную из нашего шейдера. Для простоты выберем не
    самый продуктивный, но зато самый интересный способ.
  

 Итак, находим наш рукописный шейдер
    textured\_noise.sl и переименовываем в aov.sl (AOV означает
    Arbitrary Output Variable – общепринятое название используемой нами
    в данном случае техники):
  
\begin{lstlisting}[frame=single, framerule=0pt, framesep=10pt, xleftmargin=10pt, xrightmargin=10pt]

surface
    textured_noise ( float freq=100; )
  

{
  

  Ci = texture("texture.tx", s,
    t)*noise(freq*s,freq*t);
  

 }
\end{lstlisting}
  

 В код этого шейдера нужно внести некоторые
    дополнения, с тем, чтобы рендерер мог увидеть внутренние
    переменные:
  
\begin{lstlisting}[frame=single, framerule=0pt, framesep=10pt, xleftmargin=10pt, xrightmargin=10pt]

surface
    aov ( float freq=100;
  

               output varying color tex = 0;
  

               output varying float noi = 0;)
  

{
  

  tex = texture("texture.tx", s, t);
  

  noi = noise(freq*s,freq*t);
  

  Ci
    = tex*noi;
  

 }
\end{lstlisting}
  

 Исправления, которые мы только что внесли, очевидны
    – мы создали две новые переменные, которые специальным образом
    объявили в заголовке шейдера – и этим переменным присвоили значения
    внутри шейдера.
  

 Дальше начинается самое интересное. Компилируем
    шейдер, копируем получившийся файл aov.slo и используемую текстуру
    texture.tx в папку с майским проектом, прямо в корень (файл с
    исходным кодом шейдера aov.sl можно не копировать).
    Возвращаемся в Maya, и в окне Slim делаем File=>Import
    Appearance=> Import Appearance… (или
    нажимаем Ctrl-I). Выбираем наш aov.slo и вуаля – мы только что
    загрузили написаный руками шейдер в Slim. Импортированый таким
    образом шейдер имеет массу ограничений – например, к его параметрам
    нельзя присоединять другие темплейты, как мы это делали раньше – но
    тем не менее, удобство налицо.
  

 Для
    продвинутых: аналогичную операцию
    выполняет утилита командной строки toslim.
  

 Дальнейшая процедура тривиальна – присоединяем
    шейдер к овечке и рендерим:
  

 \gr{image131}
  

 Во-о-он у неё на ушке наша надпись видна – видите?
    Но мы сейчас интересуемся совсем не этим – мы хотим вывести с
    отдельные слои переменные tex и noi. В очередной раз открываем
    RenderMan Globals, удаляем старый слой и добавляем два новых, как
    показано на скриншоте:
  

 \gr{image133}
  

 Поскольку мы собираемся использовать не глобальные,
    а локальные переменные нашего шейдера, то в списке переменных их
    нет; соответствующие значения в поле Mode придётся скопировать из
    заголовка исходного кода шейдера – а именно,
  

varying
    color tex
  

 и
  

varying
    float noi
  

 Отправляем на рендеринг и получаем в папке rmanpix
    2 новых файла - untitled.noise.0001.tif:
  

 \gr{image135}
  

 в который вывелось значение переменной noi нашего
    шейдера (а поскольку шейдер мы применили только к голове овечки, то
    и видим мы его только на голове) – в этой переменной мы сохранили
    величину нойза до того, как перемножили её на текстуру – и файл
    untitled.texture.0001.tif (в него мы выводили текстуру без
    нойза):
  

 \gr{image137}
  

 В этом месте с людьми обычно случается кондратий и
    начинается вакханалия неконтролируемого рендеринга – в отдельные
    слои выводится всё, что только можно, а затем сцена фактически
    заново собирается в композере, что даёт невиданные ранее
    возможности настройки и изменения финального изображения после
    рендеринга – без самого рендеринга.
  

 Полученные таким образом слои можно также
    использовать в качестве текстуры для шейдера. Например, вы можете
    просчитать ambient occlusion для всей сцены один раз, а потом
    подмешивать его в качестве текстуры в свой шейдер. Или всё-таки
    делать это в композере – на ваше усмотрение.
  

 Для
    любопытных: Среди крупных студий
    до сих пор нет консенсуса в том, какие именно слои и значения каких
    именно переменных нужны и важны для композа. Философия Weta
    Digital, например, в этом случае состоит в том, что рендерить нужно
    в максимально готовом к композитингу виде, с тем, чтобы композеры
    не воссоздавали сцену заново из множества слоёв, повторяя
    фактически работу рендерера. Например, в ходе работы над фильмом
    “I, Robot” Digital Domain и Weta Digital разделили между собой
    планы с участием роботов NS5; при этом Weta считала отдельно только
    металические и пластмассовые части; DD считал diffuse, specular и
    отражение в отдельные файлы, и в результате получая десятки слоёв
    на каждого робота.
  

 Для
    продвинутых: рендеринг по слоям –
    всего лишь один из множества методов борьбы за уменьшение времени
    получения конечного результата при рендеринге. Ещё один похожий на
    него приём из той же серии – это всевозможное кэширование значений
    и последующее использование этих кэшей рендерером без долгого
    пересчёта. Копайтесь в документации к вашему рендереру – например,
    для Prman ключевыми
    словами будут {\it cache и} {\it brick.}

  \section*{Мантра про raytracing.}
  

 В жизни любого TD есть несколько
    извечных дилем. Например, такая – человеческое время стоит гораздо
    дороже, чем время машинное, и ни того, ни другого никогда не
    хватает. Или вот ещё одна – нам нужен мегакачественный продукт, и
    причём ещё вчера.
  

 При чём здесь рейтрейсинг, спросит наш читатель?  
  

 Да при нём, при самом – при результате
    рендеринга.
  

 Существуют множество способов имитировать raytracing и GI. Например, в
    случае зеркального отражения – поставить камеру в необходимую
    точку, а затем результат рендеринга наложить как текстурную карту
    на само зеркало. Или вместо Global Illumination –
    по-умному расположить в сцене уйму источников света.
  

 К сожалению, при всей возможной автоматизации этих
    триков – они всё равно требуют больших человеческих затрат опытных
    сотрудников – а их время стоит что? Правильно – дороже машинного.
    Что ещё хуже – очень часто эти трики не срабатывают или срабатывают
    не так, как хотелось бы. И тогда приходится прибегать к честному
    рейтрейсингу и его родственникам – ambient occlusion и GI.
  

 Без рейтрейсинга в современном кино- и
    видео-бизнесе, как говорится – никуда. Трейсить ambient occlusion
    придётся всё равно – это один из основных приёмов быстрого
    достижения столь желанной фотореалистичности. Но у этой медали есть
    обратная сторона - простое включение рейтрейсинга в сцене
    увеличивает время просчета в разы, каким бы ультраоптимизированным
    рендерером вы не пользовались, и prman не
    исключение, а скорее яркий пример. Поэтому, если вы делаете
    компьютерную графику в действительно больших количествах – для
    рекламы, кино или телевидения – постарайтесь поменьше raytrace’ить
    (а также ambient occlude’ить, global illuminate’ить и final
    gather’ить).

    А если уж совсем припекло, стекло не получается стеклянным, а всего
    лишь тоскливо стекловидным – воспользуйтесь предыдущим советом и
    сразу записывайте результаты в отдельные слои, а потом используйте
    записанное, вместо того, чтобы каждый раз производить длительные
    расчёты заново.
  

 Для
    продвинутых: строго говоря, prman
    до сих пор не является рейтрейсером, даже несмотря на то, что
    подобную функциональность он реализует в полной мере. Основным
    алгоритмом, который он использует при рендеринге, является REYES;
    этот алгоритм полировался и оптимизировался в течение более чем
    десятка лет. Поддержка raytracing была добавлена в Renderman Pro
    Server не так уж и давно, и, судя по описаниям и опубликованным
    статьям, в ядро рендерера она не встроена. Таким образом, если вы
    не используете эту поддержку – всё ещё запускается старый добрый
    ультабыстрый REYES-движок; если вам хочется потрейсить – к нему
    присоединяется новый; движки интенсивно обмениваются данными и даже
    совместно используют некоторые структуры в памяти – но всё равно,
    ядро prman до сих пор остаётся старым, а raytracing – это
    всего-лишь добавление к нему, существенно замедляющее
    процесс.